package gogo

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
)

// HTTPContext 提供给客户使用的扩展指针，包含了原始的http.ResponseWriter和http.Request，同时还提供了封装好的一系列方便函数
type HTTPContext struct {
	w         http.ResponseWriter
	r         *http.Request
	urlParams map[string]string
	body      []byte
}

// GetW 获取原始的http.ResponseWriter
func (ctx *HTTPContext) GetW() http.ResponseWriter {
	return ctx.w
}

// GetR 获取原始的http.Request
func (ctx *HTTPContext) GetR() *http.Request {
	return ctx.r
}

// GetResponseWriter 获取原始的http.ResponseWriter
func (ctx *HTTPContext) GetResponseWriter() http.ResponseWriter {
	return ctx.w
}

// GetRequest 获取原始的http.Request
func (ctx *HTTPContext) GetRequest() *http.Request {
	return ctx.r
}

// GetPostBody 获取POST请求的内容
func (ctx *HTTPContext) GetPostBody() []byte {
	if ctx.body == nil {
		ctx.body, _ = ioutil.ReadAll(ctx.r.Body)
		ctx.r.Body.Close()
	}

	return ctx.body
}

// GetString 获取GET请求?后面的String参数值,
// key string: 参数名称
// def ...string: 参数可选，当获取参数值无效的时候，便返回这里提供的值
func (ctx *HTTPContext) GetString(key string, def ...string) string {
	strv := strings.Join(ctx.r.Form[key], "")
	if len(strv) == 0 && len(def) > 0 {
		return def[0]
	}
	return strv
}

// GetInt 获取GET请求?后面的int参数值
// key string: 参数名称
// def ...int: 参数可选，当获取参数值无效的时候，便返回这里提供的值
func (ctx *HTTPContext) GetInt(key string, def ...int) int {
	strv := strings.Join(ctx.r.Form[key], "")
	if len(strv) == 0 && len(def) > 0 {
		return def[0]
	}

	if result, err := strconv.Atoi(strv); err == nil {
		return result
	}
	return 0
}

// GetInt64 获取GET请求?后面的int64参数值
// key string: 参数名称
// def ...int64: 参数可选，当获取参数值无效的时候，便返回这里提供的值
func (ctx *HTTPContext) GetInt64(key string, def ...int64) int64 {
	strv := strings.Join(ctx.r.Form[key], "")
	if len(strv) == 0 && len(def) > 0 {
		return def[0]
	}

	if result, err := strconv.ParseInt(strv, 10, 64); err == nil {
		return result
	}
	return 0
}

// GetPathParam 获取GET请求路径中的String格式参数值，例如/restful/:table_name/:id
// key string: 参数名称
func (ctx *HTTPContext) GetPathParam(key string) string {
	return ctx.urlParams[key]
}

// WriteString 将String格式的数据输出给HTTP客户端
// content string: 需要输出HTTP客户端的String格式数据
func (ctx *HTTPContext) WriteString(content string) {
	fmt.Fprintf(ctx.w, content)
}

// WriteByte 将[]byte格式的数据输出给HTTP客户端
// content []byte: 需要输出HTTP客户端的[]byte格式数据
func (ctx *HTTPContext) WriteByte(content []byte) {
	ctx.w.Write(content)
}

// WriteHeader 指定Status到头部输出给HTTP客户端
// status int: HTTP状态, 参考net\http\status.go
func (ctx *HTTPContext) WriteHeader(status int) {
	ctx.w.WriteHeader(status)
}

// WriteJSON 将Struct结构体的数据转换成Json输出给HTTP客户端
// v interface{}: 需要输出HTTP客户端的Struct结构体数据
func (ctx *HTTPContext) WriteJSON(v interface{}) error {
	b, err := json.Marshal(v)
	if err == nil {
		ctx.WriteString(string(b))
		return nil
	}

	ctx.WriteString(err.Error())
	return err
}

// WriteExecute 将官方原始的ParseFiles和Execute接口做了合并
// data interface{}: 渲染模板需要的数据
// filenames ...string: 模板文件路径
func (ctx *HTTPContext) WriteExecute(data interface{}, filenames ...string) error {
	tmpl, err := template.ParseFiles(filenames...)

	if err == nil {
		tmpl.Execute(ctx.w, data)
		return nil
	}

	return err
}

// Redirect 网页跳转
// url string: 跳转的url
// status int: 需要写入头部的状态，例如http.StatusOK
func (ctx *HTTPContext) Redirect(url string, status int) {
	http.Redirect(ctx.w, ctx.r, url, status)
}
